﻿using Avalonia.BiliBili.Libs;
using Avalonia.BiliBili.Libs.Extensions;
using Avalonia.BiliBili.Units;
using Avalonia.Controls;
using Avalonia.Extensions.Controls;
using Avalonia.Media.Imaging;
using Bilibili.Common;
using Bilibili.Common.Database;
using Bilibili.Common.Log;
using Bilibili.Model.Common;
using Bilibili.Model.Resource;
using Bilibili.Model.User;
using Bilibili.WebClient;
using Bilibili.WebClient.Api;
using DynamicData;
using PCLUntils;
using PCLUntils.Objects;
using PCLUntils.Plantform;
using ReactiveUI;
using SkiaSharp;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Threading.Tasks;
using System.Timers;
using ZXing;
using ZXing.Common;
using ZXing.SkiaSharp;

namespace Avalonia.BiliBili.ViewModels
{
    public sealed class LoginViewModel : ViewModelBase
    {
        private string sessionId;
        private string captchaKey;
        public string Passwd { get; set; }
        public string Account { get; set; }
        public int LoginType { get; set; } = 2;
        private Bitmap _qrSource;
        public Bitmap QrSource
        {
            get => _qrSource;
            set => this.RaiseAndSetIfChanged(ref _qrSource, value);
        }
        private string phone;
        public string Phone
        {
            get => phone;
            set => this.RaiseAndSetIfChanged(ref phone, value);
        }
        private int cnIndex = 0;
        public int CnIndex
        {
            get => cnIndex;
            set => this.RaiseAndSetIfChanged(ref cnIndex, value);
        }
        private string remainTime = "发送";
        public string RemainTime
        {
            get => remainTime;
            set => this.RaiseAndSetIfChanged(ref remainTime, value);
        }
        private string smsCode;
        public string SmsCode
        {
            get => smsCode;
            set => this.RaiseAndSetIfChanged(ref smsCode, value);
        }
        private string _Message;
        public string Message
        {
            get => _Message;
            set => this.RaiseAndSetIfChanged(ref _Message, value);
        }
        private ObservableCollection<CountryItemModel> countries = [];
        public ObservableCollection<CountryItemModel> Countries
        {
            get => countries;
            set => this.RaiseAndSetIfChanged(ref countries, value);
        }
        private Timer Timer { get; }
        public Window Window { get; set; }
        private string AuthCode { get; set; }
        private UserClient UserManager { get; set; }
        public event CallbackObject Callback;
        public ReactiveCommand<string, Unit> OnButtonClick { get; }
        public LoginViewModel(Window window)
        {
            Window = window;
            Timer = new Timer(1000);
            Timer.Elapsed += Timer_Elapsed;
            UserManager = new UserClient { SsoCallback = SsoCallback };
            OnButtonClick = ReactiveCommand.Create<string>(ButtonClick);
        }
        private void SsoCallback(string url) => Navigater.OpenWebView(url);
        private void Timer_Elapsed(object? sender, ElapsedEventArgs e)
        {
            RunOnUiThread(async () =>
            {
                switch (LoginType)
                {
                    case 0:
                        var result = await UserManager.PollQRAuthInfo(AuthCode);
                        if (result.IsSuccess())
                        {
                            if (result.Data.IsEmpty())
                                await QrLogin();
                            else
                            {
                                Timer.Stop();
                                var info = await UserManager.GetAccessKey();
                                if (info.Success)
                                {
                                    SettingHelper.AccessKey = info.Data.Access_token;
                                    SettingHelper.UserID = info.Data.Mid;
                                    SettingHelper.LoginExpires = DateTime.Now.AddDays(90);
                                    SettingHelper.RefreshToken = result.Data.Refresh_Token;
                                    await UserManager.GetUserInfo(info.Data.Mid, true);
                                    StaticValue.ApiClient.UpdateClient();
                                    StaticValue.LoginEvent?.Execute();
                                    Callback?.Invoke(this);
                                }
                                else
                                    await MessageBox.Show("提示", "登录失败！" + info.Message);
                            }
                        }
                        break;
                    case 1:
                        if (int.TryParse(RemainTime.Replace("S", ""), out int time))
                        {
                            if (time >= 1)
                            {
                                time--;
                                RemainTime = $"{time}S";
                            }
                            else
                                RemainTime = "发送";
                        }
                        break;
                }
            });
        }
        private void ButtonClick(string obj)
        {
            RunOnUiThread(async () =>
            {
                switch (obj)
                {
                    case "web":
                        {
                            if (PlantformUntils.System == Platforms.Linux || PlantformUntils.IsArmArchitecture)
                                await MessageBox.Show("提示", "当前设备暂不支持该登录方式！");
                            else
                            {
                                Navigater.OpenWebView("https://passport.bilibili.com/login");
                                Window.Close();
                            }
                        }
                        break;
                    case "login":
                        switch (LoginType)
                        {
                            case 1:
                                await SmsLogin();
                                break;
                            case 2:
                                await Login();
                                break;
                        }
                        break;
                    case "send":
                        if (!string.IsNullOrEmpty(Phone) && RemainTime == "发送")
                        {
                            var country = Countries.ElementAt(CnIndex);
                            await SendSms(country.Country_Code);
                        }
                        break;
                }
            });
        }
        private async Task<int> SendSms(string countryCode, GeetestInfo geetest = null)
        {
            try
            {
                int code = -1;
                ReturnModel<ValueTuple<int, string, string>> result;
                if (geetest == null || geetest.Success == 0)
                    result = await UserManager.SendSMS(countryCode, Phone);
                else
                    result = await UserManager.SendSMS(countryCode, Phone, sessionId, geetest);
                if (result.Success)
                {
                    sessionId = result.Data.Item3;
                    if (result.Data.Item1 == 0)
                    {
                        captchaKey = result.Data.Item2;
                        Timer.Start();
                        RemainTime = "60S";
                        code = 0;
                    }
                    else
                    {
                        var url = $"https://michael-eddy.gitee.io/blog/bili_gt2.html{new Uri(result.Data.Item2).Query}&app=uwp";
                        PopupToast.Show("需要验证后继续");
                        Navigater.OpenWebView(Window, url);
                        code = await SendSms(countryCode, StaticValue.GeetestData);
                    }
                }
                else
                    PopupToast.Show("发送验证码失败," + result.Message);
                return code;
            }
            finally
            {
                StaticValue.GeetestData = null;
            }
        }
        public async Task<int> GetCountry()
        {
            try
            {
                var result = await UserManager.GetCountry();
                if (result.Success)
                {
                    Countries.AddRange(result.Data);
                    CnIndex = 0;
                    var item = Countries.FirstOrDefault(x => x.Country_Code == "86");
                    return Countries.IndexOf(item);
                }
                else
                    PopupToast.Show("获取地区失败," + result.Message);
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("GetCountry", ex);
            }
            return 0;
        }
        public async Task SmsLogin()
        {
            try
            {
                if (Phone.IsNotEmpty() && SmsCode.IsNotEmpty())
                {
                    var code = Countries.ElementAt(CnIndex)?.Country_Code;
                    var callbackModel = await UserManager.SmsLogin(code, Phone, SmsCode, sessionId, captchaKey);
                    await HandleLogin(callbackModel);
                }
                else
                    await MessageBox.Show(Window, "提示", "请输入验证码！");
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("SmsLogin", ex);
            }
        }
        public async Task QrLogin()
        {
            try
            {
                var result = await UserManager.GetQRAuthInfo();
                if (result.IsSuccess())
                {
                    BarcodeWriter barcodeWriter = new BarcodeWriter
                    {
                        Format = BarcodeFormat.QR_CODE,
                        Options = new EncodingOptions
                        {
                            Margin = 1,
                            Height = 200,
                            Width = 200
                        }
                    };
                    using var img = barcodeWriter.Write(result.Data.Url);
                    using var ms = new MemoryStream();
                    img.Encode(SKEncodedImageFormat.Png, 100).SaveTo(ms);
                    ms.Position = 0;
                    ms.Flush();
                    AuthCode = result.Data.Qrcode_Key;
                    var bitmap = new Bitmap(ms);
                    QrSource = bitmap;
                    Timer.Start();
                }
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("QrLogin", ex);
            }
        }
        public async Task Login()
        {
            try
            {
                if (Account.IsNotEmpty() && Passwd.IsNotEmpty())
                {
                    var callbackModel = await UserManager.LoginV3(Account, Passwd);
                    await HandleLogin(callbackModel);
                }
                else
                    await MessageBox.Show(Window, "提示", "请输入账号密码！");
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("Login", ex);
            }
        }
        private async Task HandleLogin(LoginCallback callbackModel)
        {
            try
            {
                switch (callbackModel.Status)
                {
                    case LoginStatus.Success:
                        {
                            var result = await UserManager.GetUserInfo(SettingHelper.UserID, true);
                            var userInfo = result.Data.Data.Card;
                            StaticValue.CurrentUserId = userInfo.Mid.ToInt64();
                            StaticValue.CurrentUserName = userInfo.Name;
                            string userHead = Path.Combine(Bilibili.Common.Extension.GetRoot(true), $"{userInfo.Mid}{Path.GetExtension(userInfo.Face)}");
                            await Current.Instance.ApiClient.GetImageStream(userInfo.Face, (bytes) =>
                            {
                                FileInfo fileInfo = new FileInfo(userHead);
                                if (fileInfo.Exists)
                                    fileInfo.Delete();
                                using var fs = fileInfo.Create();
                                fs.Write(bytes, 0, bytes.Length);
                            });
                            SettingHelper.UserHead = userHead;
                            StaticValue.CurrentUserIcon = userHead.ToBitmap();
                            StaticValue.LoginEvent?.Execute();
                            Message = "登录成功！";
                            StaticValue.ApiClient.UpdateClient();
                            Window.Close();
                            break;
                        }
                    case LoginStatus.Fail:
                    case LoginStatus.Error:
                        {
                            Message = "登录失败，失败信息：" + callbackModel.Message;
                            break;
                        }
                    case LoginStatus.NeedCaptcha:
                        {
                            Message = "登录需要验证码，请操作后继续";
                            Navigater.OpenWebView(Window, callbackModel.Url);
                            break;
                        }
                    case LoginStatus.NeedValidate:
                        {
                            Message = callbackModel.Message;
                            Navigater.OpenWebView(Window, $"https://l78z.nsapps.cn/bili_gt.html{new Uri(callbackModel.Url).Query}&app=uwp");
                            break;
                        }
                }
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("HandleLogin", ex);
            }
        }
        public override void Dispose()
        {
            base.Dispose();
            try
            {
                Timer.Stop();
                Timer.Dispose();
            }
            catch { }
        }
    }
}